#pragma once
#include <functional>
#include <pthread.h>
#include "tcp_socket.hpp"
typedef std::function<void(const std::string&, std::string*)> Handler;
struct ThreadArg {
	TcpSocket new_sock;
	std::string ip;
	uint16_t port;
	Handler handler;
};
class TcpThreadServer {
public:
	TcpThreadServer(const std::string& ip, uint16_t port) : ip_(ip), port_(port) {

	}
	bool Start(Handler handler) {
		// 1. 创建 socket;
		CHECK_RET(listen_sock_.Socket());
		// 2. 绑定端口号
		CHECK_RET(listen_sock_.Bind(ip_, port_));
		// 3. 进行监听
		CHECK_RET(listen_sock_.Listen(5));
		// 4. 进入循环
		for (;;) {
			// 5. 进行 accept
			ThreadArg* arg = new ThreadArg();
			arg->handler = handler;
			bool ret = listen_sock_.Accept(&arg->new_sock, &arg->ip, &arg->port);
			if (!ret) {
				continue;
			}
			printf("[client %s:%d] connect\n", arg->ip.c_str(), arg->port);
			// 6. 创建新的线程完成具体操作
			pthread_t tid;
			pthread_create(&tid, NULL, ThreadEntry, arg);
			pthread_detach(tid);
		}
		return true;
	}
	// 这里的成员函数为啥非得是 static?
	static void* ThreadEntry(void* arg) {
		// C++ 的四种类型转换都是什么?
		ThreadArg* p = reinterpret_cast<ThreadArg*>(arg);
		ProcessConnect(p);
		// 一定要记得释放内存!!! 也要记得关闭文件描述符
		p->new_sock.Close();
		delete p;
		return NULL;
	}
	// 处理单次连接. 这个函数也得是 static 
	static void ProcessConnect(ThreadArg* arg) {
		// 1. 循环进行读写
		for (;;) {
			std::string req;
			// 2. 读取请求
			bool ret = arg->new_sock.Recv(&req);
			if (!ret) {
				printf("[client %s:%d] disconnected!\n", arg->ip.c_str(), arg->port);
				break;
			}
			std::string resp;
			// 3. 根据请求计算响应
			arg->handler(req, &resp);
			// 4. 发送响应
			arg->new_sock.Send(resp);
			printf("[client %s:%d] req: %s, resp: %s\n", arg->ip.c_str(),
				arg->port, req.c_str(), resp.c_str());
		}
	}
private:
	TcpSocket listen_sock_;
	std::string ip_;
	uint16_t port_;
};